package com.j.sdks.bll.persistence.impl;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import com.j.sdks.bll.DataAccessException;
import com.j.sdks.bll.PageBean;
import com.j.sdks.bll.SearchResult;
import com.j.sdks.bll.persistence.BaseDataAccess;

/**
 * 
 * @author jm
 * 
 * @param <T>
 * @param <PK>
 */
// SqlSessionDaoSupport需要一个sqlSessionFactory或sqlSessionTemplate属性来设置。
// 这些被明确地设置或由Spring来自动装配。
// 如果两者都被设置了，那么SqlSessionFactory是被忽略的。
@Component("dataAccessTemplateImpl")
public class BaseDataAccessImpl<T, PK extends java.io.Serializable> extends
		SqlSessionDaoSupport implements BaseDataAccess<T, PK> {
	protected Log log = LogFactory.getLog(getClass());
	protected SqlSessionFactory sqlSessionFactory;
	protected String namespace = getClass().getName();
	protected final String dataAccessNamespace = BaseDataAccess.class
			.getName();

	@Autowired
	@Qualifier("sqlSessionFactory")
	public final void setSSF(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = sqlSessionFactory;
		setSqlSessionFactory(sqlSessionFactory);
	}

	public int insert(T obj) {
		return getSqlSession().insert(namespace + ".insert", obj);
	}

	public int delete(PK pk) {
		return getSqlSession().delete(namespace + ".delete", pk);
	}

	public int update(T obj) {
		return getSqlSession().update(namespace + ".update", obj);
	}

	public int merge(T obj) {
		return getSqlSession().insert(namespace + ".merge", obj);
	}

	public T get(PK pk) {
		return (T) getSqlSession().selectOne(namespace + ".get", pk);
	}

	// /批量更新时：数据超过10W条数据抛出java内存溢出,而数据库中实际插入了,大概8W多条数据;
	public int batch(T[] objs) {
		if (objs == null) {
			throw new DataAccessException("arguments 'objs' is null");
		}
		SqlSession sqlSession = sqlSessionFactory.openSession(
				ExecutorType.BATCH, false);
		int expectCount = objs.length;
		int actualCount = expectCount;
		try {
			sqlSession.getConnection().setAutoCommit(false);
		} catch (SQLException e) {
			e.printStackTrace();
		}

		try {
			String arg0 = namespace + ".batch";
			for (int i = 0; i < objs.length; i++) {
				sqlSession.insert(arg0, objs[i]);
			}
		} finally {
			sqlSession.close();
		}

		log.debug("expectCount:" + expectCount + ",actualCount:" + actualCount);
		return actualCount;
	}

	public boolean exist(PK pk) {
		int r = (Integer) getSqlSession().selectOne(namespace + ".exist", pk);
		return r > 0;
	}

	public SearchResult<T> search(T obj, PageBean pageBean) {

		RowBounds rb = new RowBounds(pageBean.getStartRow(),
				pageBean.getPageSize());
		
		// 查询结果
		List<T> list = null;
		int rowCount = 0;
		// 获取查询的sql
		BoundSql bs = getSqlSession().getConfiguration()
				.getMappedStatement(namespace + ".search").getBoundSql(obj);
		String rowSQL = bs.getSql();
		String rowCountSQL = "SELECT COUNT(1) FROM (" + rowSQL + ") TMP";
		List<ParameterMapping> pmList = bs.getParameterMappings();
		List<Object> parameters = new ArrayList<Object>();
		for (ParameterMapping pm : pmList) {
			Object p = bs.getAdditionalParameter(pm.getProperty());
			parameters.add(p);
		}
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//查询行数
		list = sqlSession.selectList(namespace + ".search", obj, rb);
		try {
			//计算总行数
			java.sql.Connection conn = sqlSession.getConnection();
			PreparedStatement ps = conn.prepareStatement(rowCountSQL);
			for (int i = 0; i < parameters.size(); i++) {
				ps.setObject(i + 1, parameters.get(i));
			}
			ResultSet rs = ps.executeQuery();
			if (rs.next()) {
				rowCount = rs.getInt(1);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			sqlSession.close();
		}
		SearchResult<T> searchResult = new SearchResult<T>(rowCount, list);
		return searchResult;
	}

	@Override
	public String toString() {
		return "DataAccessTemplateImpl [namespace=" + namespace + "]";
	}

}
